/*!
 * \file ast.cc
 *
 * \brief Implementations to methods declared in ast.h
 *
 * \date Started: 5/17/2010
 * \date Last Modified: 6/10/2010
 * \authors Ian Craig
 *
 * Copyright (c) 2010, Colorado State University <br>
 * All rights reserved. <br>
 * See ../../COPYING for details. <br>
 */

#include <iegenlib/iegenlib.h>

#include <iostream>
using std::cout;
using std::endl;
#include <sstream>
using std::stringstream;

namespace iegenlib{

/****************VarTuple**************/
VarTuple::VarTuple(const VarTuple& vt){
  for(list<VarExp*>::const_iterator itr = vt.vars.begin(); itr != vt.vars.end(); itr++){
    vars.push_back(new VarExp(**itr));
  }
}

VarTuple::~VarTuple(){
  for(list<VarExp*>::iterator itr = vars.begin(); itr != vars.end(); itr++){
    delete (*itr);
  }
}

VarTuple& VarTuple::operator=(const VarTuple& vt){
  VarTuple* vars = new VarTuple(vt);
  return *vars;
}

const string VarTuple::toString(){
  string temp = "";
  for(list<VarExp*>::iterator itr = vars.begin(); itr != vars.end(); itr++){
    temp += (*itr)->toString();
    temp += ",";
  }
  return "VarTuple[" + temp.substr(0, temp.size() - 1) + "]";
}

void VarTuple::apply(VisitorAdapter* visit){
  visit->VarTupleCase(this);
}

/***************************************/

/***************VarExp******************/

const string VarExp::toString(){
  if(coeff == 1){
    return "VarExp[" + id + "]";
  }else{
    string coeffString;
    stringstream str;
    str << coeff;
    coeffString = str.str();
    return "VarExp[" + coeffString + id + "]";
  }
}

void VarExp::apply(VisitorAdapter* visit){
  visit->VarExpCase(this);
}
/***************************************/

/**************FuncExp******************/
FuncExp::FuncExp(const FuncExp& fexp): coeff(fexp.coeff), name(fexp.name){
  for(list<NormExp*>::const_iterator itr = fexp.args.begin(); itr != fexp.args.end(); ++itr){
    args.push_back(new NormExp(**itr));
  }
}

FuncExp::~FuncExp(){
  for(list<NormExp*>::iterator itr = args.begin(); itr != args.end(); ++itr){
    delete (*itr);
  }
}

FuncExp& FuncExp::operator=(const FuncExp& fexp){
  FuncExp* func = new FuncExp(fexp);
  return *func;
}

const string FuncExp::toString(){
  string temp = name + "(";
  for(list<NormExp*>::iterator itr = args.begin(); itr != args.end(); ++itr){
    temp += (*itr)->toString();
    temp += ",";
  }
  temp = temp.substr(0, temp.length()-1);
  temp += ")";
  if(coeff == 1){
    return "FuncExp[" + temp + "]";
  }
  string coeffString;
  stringstream str;
  str << coeff;
  coeffString = str.str();
  return "FuncExp[" + coeffString  + temp + "]";
}

void FuncExp::apply(VisitorAdapter* visit){
  visit->FuncExpCase(this);
}
/***************************************/

/**************NormExp******************/
NormExp::NormExp(const NormExp& nexp):con(nexp.con){
  for(list<Expression*>::const_iterator itr = nexp.terms.begin(); 
      itr != nexp.terms.end(); itr++){
    VarExp* vexp;
    vexp = dynamic_cast<VarExp*>(*itr);
    if(vexp != 0){
      terms.push_back(new VarExp(*vexp));
      continue;
    }
    FuncExp* fexp;
    fexp = dynamic_cast<FuncExp*>(*itr);
    if(fexp != 0){
      terms.push_back(new FuncExp(*fexp));
      continue;
    }
    NormExp* nrexp;
    nrexp = dynamic_cast<NormExp*>(*itr);
    if(nrexp != 0){
      terms.push_back(new NormExp(*nrexp));
      continue;
    }
  }
}

NormExp::~NormExp(){
  for(list<Expression*>::iterator itr = terms.begin(); itr != terms.end();
      itr++){
    delete (*itr);
  }
}

NormExp& NormExp::operator=(const NormExp& rhs){
  NormExp* nexp = new NormExp(rhs);
  return *nexp;
}

NormExp operator+(const NormExp& op1, const NormExp& op2){
  list<Expression*>* temp = new list<Expression*>();
  /* Loops through the entirety of both operands and creates a new list of
   * Expressions for the returned NormExp
   * In order to create the new objects for the list, the Expressions must be
   * cast to their proper type: VarExp, FuncExp, or NormExp
   */
  for(list<Expression*>::const_iterator itr = op1.terms.begin(); 
      itr != op1.terms.end(); itr++){
    VarExp* vexp;
    vexp = dynamic_cast<VarExp*>(*itr);
    if(vexp != 0){
      temp->push_back(new VarExp(*vexp));
      continue;
    }
    FuncExp* fexp;
    fexp = dynamic_cast<FuncExp*>(*itr);
    if(fexp != 0){
      temp->push_back(new FuncExp(*fexp));
      continue;
    }
    NormExp* nrexp;
    nrexp = dynamic_cast<NormExp*>(*itr);
    if(nrexp != 0){
      temp->push_back(new NormExp(*nrexp));
      continue;
    }
  }
  for(list<Expression*>::const_iterator itr = op2.terms.begin();
      itr != op2.terms.end(); itr++){
    VarExp* vexp;
    vexp = dynamic_cast<VarExp*>(*itr);
    if(vexp != 0){
      temp->push_back(new VarExp(*vexp));
      continue;
    }
    FuncExp* fexp;
    fexp = dynamic_cast<FuncExp*>(*itr);
    if(fexp != 0){
      temp->push_back(new FuncExp(*fexp));
      continue;
    }
    NormExp* nrexp;
    nrexp = dynamic_cast<NormExp*>(*itr);
    if(nrexp != 0){
      temp->push_back(new NormExp(*nrexp));
      continue;
    }
  }
  int tempConst = op1.con + op2.con;
  NormExp tempNorm(temp, tempConst);
  delete temp;
  return tempNorm;
}

NormExp operator-(const NormExp& op1){
  return op1 * NormExp(-1);
}

NormExp operator-(const NormExp& op1, const NormExp& op2){
  return op1 + (-op2);
}

NormExp operator*(const NormExp& op1, const NormExp& op2){
  /* Loops through the entirety of ONE operand (the other only holds a const)
   * and creates a new list of Expressions for the returned NormExp
   * In order to create the new objects for the list, the Expressions must be
   * cast to their proper type: VarExp, FuncExp, or NormExp
   */
  list<Expression*>* temp = new list<Expression*>;
  if(!op2.hasTerms()){
    for(list<Expression*>::const_iterator itr = op1.terms.begin(); itr != op1.terms.end(); itr++){
      VarExp* vexp;
      vexp = dynamic_cast<VarExp*>(*itr);
      if(vexp != 0){
        VarExp* var = new VarExp(*vexp);
        var->setCoeff(vexp->getCoeff()*op2.con);
        temp->push_back(var);
        continue;
      }
      FuncExp* fexp;
      fexp = dynamic_cast<FuncExp*>(*itr);
      if(fexp != 0){
        FuncExp* func = new FuncExp(*fexp);
        func->setCoeff(fexp->getCoeff()*op2.con);
        temp->push_back(func);
        continue;
      }
      NormExp* nrexp;
      nrexp = dynamic_cast<NormExp*>(*itr);
      if(nrexp != 0){
        NormExp ntemp(new list<Expression*>(nrexp->getTerms()), nrexp->getConst()*op2.con);
        temp->push_back(new NormExp(-ntemp));
        continue;
      }
    }
    NormExp tempNorm(temp, op1.con*op2.con);
    delete temp;
    return tempNorm;
  }else if(!op1.hasTerms()){
    for(list<Expression*>::const_iterator itr = op2.terms.begin(); itr != op2.terms.end(); itr++){
      VarExp* vexp;
      vexp = dynamic_cast<VarExp*>(*itr);
      if(vexp != 0){
        VarExp* var = new VarExp(*vexp);
        var->setCoeff(vexp->getCoeff()*op1.con);
        temp->push_back(var);
        continue;
      }
      FuncExp* fexp;
      fexp = dynamic_cast<FuncExp*>(*itr);
      if(fexp != 0){
        FuncExp* func = new FuncExp(*fexp);
        func->setCoeff(fexp->getCoeff()*op1.con);
        temp->push_back(func);
        continue;
      }
      NormExp* nrexp;
      nrexp = dynamic_cast<NormExp*>(*itr);
      if(nrexp != 0){
        NormExp ntemp(new list<Expression*>(nrexp->getTerms()), nrexp->getConst()*op1.con);
        temp->push_back(new NormExp(-ntemp));
        continue;
      }
    }
    NormExp tempNorm(temp, op1.con*op2.con);
    delete temp;
    return tempNorm;
  }
}

NormExp operator*(const int op1, const NormExp& op2){
  return op2 * NormExp(op1);
}

NormExp operator*(const NormExp& op1, const int op2){
  return op1 * NormExp(op2);
}

const string NormExp::toString(){
  string conString;
  stringstream str;
  str << con;
  conString = str.str();
  if(this->hasTerms()){
    string temp = "(";
    for(list<Expression*>::iterator itr = terms.begin(); itr != terms.end(); itr++){
      temp += (*itr)->toString();
      temp += ',';
    }
    return "NormExp[" + temp.substr(0,temp.length()-1) + ")," + conString + ']';
  }
  return "NormExp[" + conString + ']';
}

void NormExp::apply(VisitorAdapter* visit){
  visit->NormExpCase(this);
}
/****************************************/

/***************Constraint***************/
void Constraint::apply(VisitorAdapter* visit){
  visit->ConstraintCase(this);
}
/***************************************/

/***************Equality****************/
void Equality::apply(VisitorAdapter* visit){
  visit->EqualityCase(this);
}
/***************************************/

/***************Inequality**************/
void Inequality::apply(VisitorAdapter* visit){
  visit->InequalityCase(this);
}
/***************************************/

/****************Conjunction************/
Conjunction::Conjunction(const Conjunction& con){
  inVars=new VarTuple(*con.inVars);
  if(con.outVars != NULL){
    outVars= new VarTuple(*con.outVars);
  }
  else{
    outVars= NULL;
  }
  for(list<Constraint*>::const_iterator itr = con.constraints.begin(); itr != con.constraints.end(); ++itr){
    Equality* eq;
    eq = dynamic_cast<Equality*>(*itr);
    if(eq != 0){
      constraints.push_back(new Equality(*eq));
      continue;
    }
    Inequality* ineq;
    ineq = dynamic_cast<Inequality*>(*itr);
    if(ineq != 0){
      constraints.push_back(new Inequality(*ineq));
      continue;
    }
  }
}

Conjunction& Conjunction::operator=(const Conjunction& con){
  Conjunction* conj = new Conjunction(con);
  return *conj;
}

Conjunction::~Conjunction(){
  delete inVars;
  if(outVars != NULL){
    delete outVars;
  }
  for(list<Constraint*>::iterator itr = this->constraints.begin(); itr != this->constraints.end(); itr++){
    delete (*itr);
  }
} 

const string Conjunction::toString(){
  string temp = inVars->toString() + ',';
  if(outVars != NULL){
	temp += outVars->toString() + ',';
  }
  for(list<Constraint*>::iterator itr = this->constraints.begin(); itr != this->constraints.end(); itr++){
    temp += (*itr)->toString();
    temp += " and ";
  }
  return "Conjunction[" + temp.substr(0, temp.size() - 5) + "]";
}

void Conjunction::apply(VisitorAdapter* visit){
  visit->ConjunctionCase(this);
}
/***************************************/

/**************PresForm*****************/
void PresForm::apply(VisitorAdapter* visit){
  visit->PresFormCase(this);
}
/***************************************/

/**************PresSet******************/
PresSet::PresSet(const PresSet& ps):symbolics(ps.symbolics){
  for(list<Conjunction*>::const_iterator itr = ps.conjuncts.begin(); itr != ps.conjuncts.end(); itr++){
    conjuncts.push_back( new Conjunction( **itr ));
  }
}

PresSet::~PresSet(){
  for(list<Conjunction*>::const_iterator itr = this->conjuncts.begin(); itr != this->conjuncts.end(); itr++){
     delete *itr;
  }
}

PresSet& PresSet::operator=(const PresSet& ps){
  PresSet* newps = new PresSet(ps);
  return *newps;
}

const string PresSet::toString(){
  string temp="PresSet[";
  string temp1 = "";
  for(list<Conjunction*>::iterator itr = this->conjuncts.begin(); itr != this->conjuncts.end(); itr++){
     temp1 += (*itr)->toString();
     temp1 += ",";
  }
  temp +=temp1;
  temp +="Symbolics[";

  bool first = true;
  for(list<string>::iterator itr = symbolics.begin(); itr != symbolics.end(); itr++){
    if(!first){
      temp += ',';
    }else first = false;
    temp += *itr;
  }
  temp += "]]";

  return temp;
}

void PresSet::applyConjunct(VisitorAdapter* visitor){
  for(list<Conjunction*>::const_iterator itr = conjuncts.begin(); itr != conjuncts.end(); itr++){
    (*itr)->apply(visitor);
  }
}

void PresSet::apply(VisitorAdapter* visit){
  visit->PresSetCase(this);
}
/***************************************/

/***********PresRelation****************/
PresRelation::PresRelation(const PresRelation& pr):symbolics(pr.symbolics){
  for(list<Conjunction*>::const_iterator itr = pr.conjuncts.begin(); itr != pr.conjuncts.end(); itr++){
    conjuncts.push_back( new Conjunction( **itr ));
  }
}

PresRelation& PresRelation::operator=(const PresRelation& pr){
  PresRelation* newpr = new PresRelation(pr);
  return *newpr;
}

PresRelation::~PresRelation(){
   for(list<Conjunction*>::const_iterator itr = this->conjuncts.begin(); itr != this->conjuncts.end(); itr++){
     delete *itr;
   }
}

const string PresRelation::toString(){
  string temp = "PresRelation[";
  string temp1 = "";
  for(list<Conjunction*>::iterator itr = this->conjuncts.begin(); itr != this->conjuncts.end(); itr++){
    temp1 += (*itr)->toString();
    temp1 += ",";
  }
  temp +=temp1;
  temp +="Symbolics[";

  bool first = true;
  for(list<string>::iterator itr = symbolics.begin(); itr != symbolics.end(); itr++){
    if(!first){
      temp += ',';
    }else first = false;
    temp += *itr;
  }
  temp += "]]";
  return temp;
}

void PresRelation::applyConjunct(VisitorAdapter* visitor){
  for(list<Conjunction*>::const_iterator itr = conjuncts.begin(); itr != conjuncts.end(); itr++){
    (*itr)->apply(visitor);
  }
}

void PresRelation::apply(VisitorAdapter* visit){
  visit->PresRelationCase(this);
}
/****************************************/

}//end namespace iegenlib
